home *** CD-ROM | disk | FTP | other *** search
Text File | 1998-06-06 | 11.1 KB | 290 lines | [TEXT/CWIE] |
- // Program Author: Paul Baxter
- // pbaxter@assistivetech.com
- //
- //
-
- #include <Ctype.h>
- #include <DeskBus.h>
- #include <Speech.h>
- #include <Retrace.h>
-
- #include "filter.h"
- #include "speech.h"
- #include "pref.h"
- #include "globals.h"
-
-
- // ***********************************************************************************
- // ***********************************************************************************
-
- /*
- The Not-So-Simple FAT jGNEFilter
- Original Code by Matt Slot (fprefect@umich.edu), 6/2/95
- with help and criticism (lots!) from Ed Wynne (arwyn@umich.edu).
- modified by Paul Baxter (pbaxter@assistivetech.com).4/17/97
-
- Quick Intro
- Since jGNEFilters are nasty things in 68k, and pretty much impossible
- in PPC, writing simple and cross-compiling handler code is also. To
- facilitate use of this, I have written some interface routines to hide
- some of the complexity from the application programmer.
-
- / Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData);
- | Installs a jGNEFilter which can properly call a callback from within
- | the application. The callback handles incoming events wither either
- | 68k or PPC code, and gets passed the data pointer you send from here.
- | Install() returns a pointer the jGNEFilter installed in the system heap
- | or NIL to indicate an error.
-
- / Ptr ReleaseEventFilter(Ptr filterProc);
- | Pass in the pointer to the jGNEFilter, and this routine will disable
- | the handling and try to dispose of its storage in the System Heap.
- | You *must* do this if your helper function is going to disappear when
- | the application closes.
-
- / asm void EventFilter(void);
- | This is *not* the installed jGNEFilter... well not really. There is no way
- | to get the same code to compile on both 68K and PPC, so this routine is
- | just a demo function. The real code has been disassembled from the 68k
- | source and placed into a Hex String suitable for StuffHex()'ing into the
- | System Heap on both 68K and PPC machines. Note that the code simply calls
- | the ProcPtr for the Helper function blindly... whether 68k or PPC.
- | WARNING: Changing the EventFilter() will do nothing! -- to apply changes,
- | you need to take the modified 68K machine code and refresh the declared
- | kGNEFilterHexData string constant.
-
- / void EventFilterHelper(EventRecord *theEvent, Ptr helperData);
- | This callback is the workhorse of the event filter. Once installed, this
- | routine sees every event that gets harvested and has an opportunity to
- | modify the record before the front application gets to see it. Ideally
- | this function can do the necessary work itself or pass off the event info
- | to the home application.
- | WARNING: For 68k code, the routine will be called from the current app's
- | context (A5/Globals, Rsrc File, HeapZone). PPC code will have a valid RTOC
- | (Globals access) but not Rsrc File, HeapZone, etc.
-
-
- How this all works:
-
- Since I didn't want to write the jGNEFilter in C (OK, I couldn't figure out
- how to), the code is installed as raw 68k by StuffHex()'ing a precompiled
- routine -- identical to the one as declared below -- into the System Heap.
-
- The jGNEFilter keeps 3 pieces of data inline: the next filter in the chain,
- a pointer to the helper routine, and some extra data to pass to the helper.
- Most importantly, we may not be able to remove the filter from the calling
- chain... the architecture just doesn't permit it! If we are able to safely
- pull the filter out, we do. Otherwise the next best solution is to keep a flag,
- that we can clear when we want to disable the functionality -- in fact, we
- set or clear the pointer to the Helper Proc as the flag.
-
- Finally, the helper function is the meat of our jGNEFilter; it does the work
- of the active filter. In the case of a 68K helper, it is accessed via a
- simple ProcPtr. In the case of a PPC helper, the installer sets up a valid
- RoutineDescriptor (in the System Heap with the jGNEFilter) to invoke a
- MixedMode switch between the 68K caller (filter) and PPC routine (helper).
- Again, when releasing the filter the handler disposes the descriptor and
- clear the inline ProcPtr/flag, since the helper function will probably be
- disappearing when the application quits.
-
- If you are picking out events to handle within your app, my suggestion is to
- keep a secondary Queue of events in the System Heap -- remember, you must
- allocate new EventRecords (since the current event belongs to the calling
- app) into the System Heap (you need a heap that both the current process and
- your own process can access). Given these events, your main event loop can
- suck out clicks or keydowns for dispatching internally and safely within your
- own context.
-
- Also, Text Service windows don't receive Activate or Update events... you
- must check for those manually within your own event loop and handle them.
-
- */
-
- // ***********************************************************************************
- // * InstallEventFilter
- // * install our event filter
- // ***********************************************************************************
- Ptr InstallEventFilter(FilterHelperUPP helperProc, Ptr helperData)
- {
- Ptr filterProc, data;
-
- // Create a duplicate function in the System Heap (so its *alway* there) and
- // copy the data across. Note: even though it is technically a function, we
- // can still treat it as data safely until it has been installed and called.
- filterProc = NewPtrSys(sizeof(kGNEFilterHexData)/2 + 1);
- if (! filterProc) return(0);
- StuffHex(filterProc, kGNEFilterHexData);
-
- // Get and install the current filter as the next filter in the chain.
- data = (Ptr) LMGetGNEFilter();
- BlockMove(&data, filterProc + kNextFilterOffset, sizeof(data));
-
- // Get and install the Helper function to do the real work (and as a flag to
- // indicate we are in business and accepting events). Remember that if we
- // generating PPC code, it is necessary to establish a Routine Descriptor.
- SetZone(SystemZone());
- data = (Ptr) NewFilterHelperProc(helperProc);
- if (! data) return(0);
- BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
- SetZone((Zone*)ApplicationZone());
-
- // If the caller wants to pass data to the jGNEFilter Helper function. This
- // pointer (or handle if desired) *must* be allocated in the System Heap
- // if you don't plan on releasing the Filter before quitting. If you plan
- // on releasing the filter, then either the App or Sys heap will suffice.
- data = helperData;
- BlockMove(&helperData, filterProc + kEventHelperDataOffset, sizeof(data));
-
- // Install us, we are ready to do some work!
- LMSetGNEFilter((GNEFilterUPP) filterProc);
-
- return(filterProc);
- }
-
- // ***********************************************************************************
- // * ReleaseEventFilter
- // * remove our event filter
- // ***********************************************************************************
- Ptr ReleaseEventFilter(Ptr filterProc)
- {
- Ptr data;
-
- if (! filterProc)
- return(0);
-
- // Clear the Helper location as an indicator that we have closed up shop. The
- // filter itself may lingers in the System Heap until shutdown unless we can
- // find a way to extract it from the chain (see below). On the other hand, the
- // filter has been written so that if the Helper function pointer is NIL, the
- // filter will do nothing at all. Let's zero it out for that (hopeful) case.
- BlockMove(filterProc + kEventHelperOffset, &data, sizeof(data));
- if (data)
- DisposeRoutineDescriptor((UniversalProcPtr) data);
- data = 0;
- BlockMove(&data, filterProc + kEventHelperOffset, sizeof(data));
-
- // If the installed filterProc is the first one in the chain, then we should
- // be able remove it and replace it with the next one (the one we would
- // normally jump to). If we can dispose the filterProc buffer, then we can
- // can recover those 50 bytes that remain in the System Heap.
- // Thanks to HoverBar's Guy Fullerton (hedgeboy@realm.net) for the suggestion.
- if (filterProc == (Ptr) LMGetGNEFilter()) {
- // Remove our filterProc from the chain.
- BlockMove(filterProc + kNextFilterOffset, &data, sizeof(data));
- LMSetGNEFilter((GNEFilterUPP) data);
-
- BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
- DisposePtr(filterProc);
- }
- else {
- // Grab the data that was passed when initialized or as set in the Helper
- // function. The caller can then deallocate it if desired or necessary.
- BlockMove(filterProc + kEventHelperDataOffset, &data, sizeof(data));
- }
-
- return(data);
- }
-
- // ***********************************************************************************
- // * EventFilter
- // Eventfilter 68K code only
- // ***********************************************************************************
- #if GENERATING68K
- asm void EventFilter()
- {
- bra.s Continue
-
- Next_Filter:
- dc.l 0 // Saved Address of Next jGNEFilter in the chain.
- // We jump directly to it, no JSR's or RTS's.
- Event_Helper:
- dc.l 0 // Pointer to Helper function in our application.
- // We clear it to NIL when we quit as a flag
- Event_Helper_Data:
- dc.l 0 // Promised storage for the Helper function,
- // which can modify the pointer dynamically.
- Continue:
-
- fralloc
-
- // Save the Volatile registers for safety
- movem.l d0-d2/a0-a2, -(a7)
-
- // Load the Helper from Inline Storage and test it. If Helper is NIL,
- // then our handler was released -- and we just jump to the next Filter
- move.l Event_Helper, a0
- move.l a0, d0
- tst.l d0
- beq End_Filter
-
- // Straight C Calling Conventions, call the Helper function
- move.l Event_Helper_Data, -(a7)
- move.l a1, -(a7)
- move.l Event_Helper, a0
- jsr (a0)
- add.l #8, a7
-
- End_Filter:
-
- // Clean up the same way we got here
- movem.l (a7)+, d0-d2/a0-a2
-
- frfree
-
- // Jump to the next filter in the chain
- move.l Next_Filter, a0
- jmp (a0)
- }
- void EndEventFilter() { }
- #endif GENERATING68K
-
-
- // ***********************************************************************************
- // * EventFilterHelper
- // * heart of our filter
- // ***********************************************************************************
- void EventFilterHelper(EventRecord *theEvent, Ptr helperData)
- {
- long saveA5;
- EvQEl *fwdEvent;
- Boolean fwdThisEvent = false;
-
- // This only does something in 68K code. We now have access to globals,
- // which PPC get for free from CFM; however, we won't have access to
- // our application's Resource file/chain or HeapZone. Be careful!
- saveA5 = SetA5((long) helperData);
-
- switch(theEvent->what) {
- case keyDown:
- // we will send this to the application
- fwdThisEvent = true;
- break;
-
- case mouseDown:
- // we will send this to the application
- fwdThisEvent = true;
- break;
-
- default:
- break;
- }
- if (fwdThisEvent) {
- // We have discovered that this event deserves full attention. Forward it to
- // our home application by Q'ing it up as a pointer in the System Heap
- fwdEvent = (EvQEl *) NewPtrSys(sizeof(*fwdEvent));
- fwdEvent->qLink = 0;
- fwdEvent->qType = evType;
- BlockMove(&theEvent->what, &fwdEvent->evtQWhat, sizeof(EventRecord));
-
- Enqueue((QElem *) fwdEvent, &gForwardedEvents);
-
- // Let our app get the CPU to handle the event quickly
- WakeUpProcess(&gPSN);
- }
-
- // Restore the (68K) context before leaving
- SetA5(saveA5);
- }
-
-
-